package com.smsc.headend.connector.utils;

import cn.hutool.core.util.StrUtil;
import com.smsc.headend.common.utils.CustomStringUtils;
import com.smsc.headend.module.protocol.cosem.enums.DlmsDataType;
import gurux.dlms.GXByteBuffer;
import gurux.dlms.GXDateTime;
import gurux.dlms.enums.ClockStatus;
import gurux.dlms.enums.DataType;
import gurux.dlms.enums.DateTimeExtraInfo;
import gurux.dlms.enums.DateTimeSkips;
import gurux.dlms.internal.GXCommon;
import lombok.extern.slf4j.Slf4j;

import java.math.BigInteger;
import java.nio.charset.Charset;

/**
 * @author wxh
 * @date 2020/12/8 16:32
 */
@Slf4j
public class GXByteBufferUtils {

    /**
     * 设置 array byte报文
     * @param buffer
     * @param count
     */
    public static void setArrayByte(GXByteBuffer buffer, int count){
        buffer.setUInt8(DlmsDataType.Array.getValue());
        buffer.setUInt8(count);
    }

    /**
     * 设置 structure byte报文
     * @param buffer
     * @param count
     */
    public static void setStructureByte(GXByteBuffer buffer, int count){
        buffer.setUInt8(DlmsDataType.Structure.getValue());
        buffer.setUInt8(count);
    }

    /**
     * 设置 boolean byte报文
     * @param buffer
     * @param bool
     */
    public static void setBooleanByte(GXByteBuffer buffer, boolean bool){
        buffer.setUInt8(DlmsDataType.Boolean.getValue());
        buffer.setUInt8(bool ? 1:0);
    }

    /**
     * 设置 enum byte报文
     * @param buffer
     * @param value
     */
    public static void setEnumByte(GXByteBuffer buffer, int value){
        buffer.setUInt8(DlmsDataType.Enum.getValue());
        buffer.setUInt8(value);
    }

    /**
     * 设置 LongUnsigned byte报文
     * @param buffer
     * @param value
     */
    public static void setLongUnsignedByte(GXByteBuffer buffer, int value){
        buffer.setUInt8(DlmsDataType.LongUnsigned.getValue());
        buffer.setUInt16(value);
    }

    /**
     * 设置 doubleLongUnsigned byte报文
     * @param buffer
     * @param value
     */
    public static void setDoubleLongUnsignedByte(GXByteBuffer buffer, Long value){
        buffer.setUInt8(DlmsDataType.DoubleLongUnsigned.getValue());
        buffer.setUInt32(value);
    }

    /**
     * 设置 unsigned byte报文
     * @param buffer
     * @param value
     */
    public static void setUnsignedByte(GXByteBuffer buffer, int value){
        buffer.setUInt8(DlmsDataType.Unsigned.getValue());
        buffer.setUInt8(value);
    }

    /**
     * 把 ascii格式的string 转成报文 如abc 转成 0x09 0x03 (0x61 0x62 0x63)
     * @param buffer
     * @param value
     */
    public static void setOctStringAsciiByte(GXByteBuffer buffer, String value){
        buffer.setUInt8(DlmsDataType.OctetString.getValue());
        if (StrUtil.isEmpty(value)) {
            buffer.setUInt8(0);
        } else {
            buffer.setUInt8(value.length());
            buffer.set(value.getBytes(Charset.forName("utf-8")));
        }
    }

    public static void setOctStringIpAddrByte(GXByteBuffer buffer, String value){
        buffer.setUInt8(DlmsDataType.OctetString.getValue());
        if (StrUtil.isEmpty(value)) {
            buffer.setUInt8(0);
        } else {
            value = CustomStringUtils.formatIpAddress(value);
            buffer.setUInt8(value.length()/2);
            buffer.setHexString(value);
        }
    }



    /**
     * 把 HEX格式的string 转成报文 如616263 转成 0x09 0x03 (0x61 0x62 0x63)
     * @param buffer
     * @param value
     */
    public static void setOctStringHexByte(GXByteBuffer buffer, String value){
        buffer.setUInt8(DlmsDataType.OctetString.getValue());
        if (StrUtil.isEmpty(value)) {
            buffer.setUInt8(0);
        } else {
            buffer.setUInt8(value.length()/2);
            buffer.setHexString(value);
        }
    }

    /**
     * 把 ascii格式的string 转成报文 如abc 转成 0x09 0x03 (0x61 0x62 0x63)
     * @param buffer
     * @param value
     */
    public static void setVisibleStringAsciiByte(GXByteBuffer buffer, String value){
        buffer.setUInt8(DlmsDataType.VisibleString.getValue());
        if (StrUtil.isEmpty(value)) {
            buffer.setUInt8(0);
        } else {
            buffer.setUInt8(value.length());
            buffer.set(value.getBytes(Charset.forName("utf-8")));
        }
    }

    /**
     * HEX字符串转换报文 如 616263 转成0x61 0x62 0x63
     * @param buffer
     * @param value
     * @return
     */
    public static void setVisibleStringHexByte(GXByteBuffer buffer,String value) {
        buffer.setUInt8(DlmsDataType.VisibleString.getValue());
        if (StrUtil.isEmpty(value)) {
            buffer.setUInt8(0);
        } else {
            buffer.setUInt8(value.length()/2);
            buffer.setHexString(value);
        }
    }

    /**
     * obis 如 1.0.98.1.2.255 转成 0100620102FF 的HEX
     * @param buffer
     * @param obisValue
     */
    public static void setOctStringObisByte(GXByteBuffer buffer,String obisValue) {
        buffer.setUInt8(DlmsDataType.OctetString.getValue());
        byte[] obisByte = GXCommon.logicalNameToBytes(obisValue);
        buffer.setUInt8(obisByte.length);
        buffer.set(obisByte);

    }

    /**
     * 把时间戳转成oct-string 格式的datetime
     * @param buffer
     * @param tv
     */
    public static void setOctStrDateTime(GXByteBuffer buffer, Long tv) {
        GXDateTime dt = GXDateTime.fromUnixTime(tv);

        buffer.setUInt8(DataType.OCTET_STRING.getValue());
        buffer.setUInt8(12);
        java.util.Calendar tm = dt.getMeterCalendar();

        // Add year.
        if (dt.getSkip().contains(DateTimeSkips.YEAR)) {
            buffer.setUInt16(0xFFFF);
        } else {
            buffer.setUInt16(tm.get(java.util.Calendar.YEAR));
        }
        // Add month
        if (dt.getExtra().contains(DateTimeExtraInfo.DST_BEGIN)) {
            buffer.setUInt8(0xFE);
        } else if (dt.getExtra().contains(DateTimeExtraInfo.DST_END)) {
            buffer.setUInt8(0xFD);
        } else if (dt.getSkip().contains(DateTimeSkips.MONTH)) {
            buffer.setUInt8(0xFF);
        } else {
            buffer.setUInt8((tm.get(java.util.Calendar.MONTH) + 1));
        }
        // Add day
        if (dt.getExtra().contains(DateTimeExtraInfo.LAST_DAY)) {
            buffer.setUInt8(0xFE);
        } else if (dt.getExtra().contains(DateTimeExtraInfo.LAST_DAY2)) {
            buffer.setUInt8(0xFD);
        } else if (dt.getSkip().contains(DateTimeSkips.DAY)) {
            buffer.setUInt8(0xFF);
        } else {
            buffer.setUInt8(tm.get(java.util.Calendar.DATE));
        }
        // Day of week.
        if (dt.getSkip().contains(DateTimeSkips.DAY_OF_WEEK)) {
            buffer.setUInt8(0xFF);
        } else {
            if (dt.getDayOfWeek() == 0) {
                int val = tm.get(java.util.Calendar.DAY_OF_WEEK);
                if (val == java.util.Calendar.SUNDAY) {
                    val = 8;
                }
                buffer.setUInt8(val - 1);
            } else {
                buffer.setUInt8(dt.getDayOfWeek());
            }
        }

        // Add time.
        if (dt.getSkip().contains(DateTimeSkips.HOUR)) {
            buffer.setUInt8(0xFF);
        } else {
            buffer.setUInt8(tm.get(java.util.Calendar.HOUR_OF_DAY));
        }
        if (dt.getSkip().contains(DateTimeSkips.MINUTE)) {
            buffer.setUInt8(0xFF);
        } else {
            buffer.setUInt8(tm.get(java.util.Calendar.MINUTE));
        }
        if (dt.getSkip().contains(DateTimeSkips.SECOND)) {
            buffer.setUInt8(0xFF);
        } else {
            buffer.setUInt8(tm.get(java.util.Calendar.SECOND));
        }
        if (dt.getSkip().contains(DateTimeSkips.MILLISECOND)) {
            // Hundredth of seconds is not used.
            buffer.setUInt8(0xFF);
        } else {
            int ms = tm.get(java.util.Calendar.MILLISECOND);
            if (ms != 0) {
                ms /= 10;
            }
            buffer.setUInt8(ms);
        }
        // devitation not used.
        buffer.setUInt16(0x8000);

        // Add clock_status
        if (!dt.getSkip().contains(DateTimeSkips.STATUS)) {
            if ((dt.getMeterCalendar().getTimeZone()
                    .inDaylightTime(dt.getMeterCalendar().getTime())
                    || dt.getStatus()
                    .contains(ClockStatus.DAYLIGHT_SAVE_ACTIVE))) {
                buffer.setUInt8(ClockStatus.toInteger(dt.getStatus())
                        | ClockStatus.DAYLIGHT_SAVE_ACTIVE.getValue());
            } else {
                buffer.setUInt8(ClockStatus.toInteger(dt.getStatus()));
            }
        } else {
            // Status is not used.
            buffer.setUInt8(0xFF);
        }

    }

    public static void setDateTime(GXByteBuffer buff, GXDateTime dt) {
        buff.setUInt8(DataType.DATETIME.getValue());
        java.util.Calendar tm = dt.getMeterCalendar();
        // Add additional date time skips.

        // Add year.
        if (dt.getSkip().contains(DateTimeSkips.YEAR)) {
            buff.setUInt16(0xFFFF);
        } else {
            buff.setUInt16(tm.get(java.util.Calendar.YEAR));
        }
        // Add month
        if (dt.getExtra().contains(DateTimeExtraInfo.DST_BEGIN)) {
            buff.setUInt8(0xFE);
        } else if (dt.getExtra().contains(DateTimeExtraInfo.DST_END)) {
            buff.setUInt8(0xFD);
        } else if (dt.getSkip().contains(DateTimeSkips.MONTH)) {
            buff.setUInt8(0xFF);
        } else {
            buff.setUInt8((tm.get(java.util.Calendar.MONTH) + 1));
        }
        // Add day
        if (dt.getExtra().contains(DateTimeExtraInfo.LAST_DAY)) {
            buff.setUInt8(0xFE);
        } else if (dt.getExtra().contains(DateTimeExtraInfo.LAST_DAY2)) {
            buff.setUInt8(0xFD);
        } else if (dt.getSkip().contains(DateTimeSkips.DAY)) {
            buff.setUInt8(0xFF);
        } else {
            buff.setUInt8(tm.get(java.util.Calendar.DATE));
        }
        // Day of week.
        if (dt.getSkip().contains(DateTimeSkips.DAY_OF_WEEK)) {
            buff.setUInt8(0xFF);
        } else {
            if (dt.getDayOfWeek() == 0) {
                int val = tm.get(java.util.Calendar.DAY_OF_WEEK);
                if (val == java.util.Calendar.SUNDAY) {
                    val = 8;
                }
                buff.setUInt8(val - 1);
            } else {
                buff.setUInt8(dt.getDayOfWeek());
            }
        }

        // Add time.
        if (dt.getSkip().contains(DateTimeSkips.HOUR)) {
            buff.setUInt8(0xFF);
        } else {
            buff.setUInt8(tm.get(java.util.Calendar.HOUR_OF_DAY));
        }
        if (dt.getSkip().contains(DateTimeSkips.MINUTE)) {
            buff.setUInt8(0xFF);
        } else {
            buff.setUInt8(tm.get(java.util.Calendar.MINUTE));
        }
        if (dt.getSkip().contains(DateTimeSkips.SECOND)) {
            buff.setUInt8(0xFF);
        } else {
            buff.setUInt8(tm.get(java.util.Calendar.SECOND));
        }
        if (dt.getSkip().contains(DateTimeSkips.MILLISECOND)) {
            // Hundredth of seconds is not used.
            buff.setUInt8(0x00);
        } else {
            int ms = tm.get(java.util.Calendar.MILLISECOND);
            if (ms != 0) {
                ms /= 10;
            }
            buff.setUInt8(ms);
        }
        // devitation not used.
//        if (dt.getSkip().contains(DateTimeSkips.DEVITATION)) {
        buff.setUInt16(0x8000);
//        } else {
//            int deviation = (dt.getMeterCalendar().get(Calendar.ZONE_OFFSET)
//                    + dt.getMeterCalendar().get(Calendar.DST_OFFSET)) / 60000;
//            if (settings != null && settings.getUseUtc2NormalTime()) {
//                buff.setUInt16(deviation);
//            } else {
//                buff.setUInt16(-deviation);
//            }
//        }
        // Add clock_status
        if (!dt.getSkip().contains(DateTimeSkips.STATUS)) {
            if ((dt.getMeterCalendar().getTimeZone()
                    .inDaylightTime(dt.getMeterCalendar().getTime())
                    || dt.getStatus()
                    .contains(ClockStatus.DAYLIGHT_SAVE_ACTIVE))) {
                buff.setUInt8(ClockStatus.toInteger(dt.getStatus())
                        | ClockStatus.DAYLIGHT_SAVE_ACTIVE.getValue());
            } else {
                buff.setUInt8(ClockStatus.toInteger(dt.getStatus()));
            }
        } else {
            // Status is not used.
            buff.setUInt8(0xFF);
        }

    }

    public static void setActionTokenData(GXByteBuffer buffer,String token) {
        BigInteger temp = new BigInteger(token);
        buffer.setUInt8(DlmsDataType.OctetString.getValue());
        buffer.setHexString("0E");
        buffer.setHexString("00");
        buffer.setHexString("00");
        buffer.setHexString("00");
        buffer.setHexString("01");
        byte[] datas = temp.toByteArray();
        for (int i = 0; i < 9 - datas.length; ++i) {
            buffer.setHexString("00");
        }
        for (int i = 0; i < datas.length && i < 9; ++i) {
            buffer.setUInt8(datas[i]);
        }
        // 海星电表下发token报文增加 00 威胜电表也支持
        buffer.setHexString("00");
    }

}
